package cn.qn.framwork.security;

import cn.qn.dto.security.MemberDTO;
import cn.qn.enums.JwtAuthEnum;
import cn.qn.framwork.utils.OutUtils;
import cn.qn.framwork.utils.SpringContextUtils;
import cn.qn.moudle.security.MemberAccount;
import io.jsonwebtoken.Jwts;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * token的校验
 * 该类继承自BasicAuthenticationFilter，在doFilterInternal方法中，
 * 从http头的Authorization 项读取token数据，然后用Jwts包提供的方法校验token的合法性。
 * 如果校验通过，就认为这是一个取得授权的合法请求
 *
 * @author Jeet  2018年6月1日 12:10:33.
 */
public class JwtAuthFilter extends BasicAuthenticationFilter {
    public JwtAuthFilter(AuthenticationManager authenticationManager) {

        super(authenticationManager);
    }

    private RedisTemplate<String, MemberDTO> redisTemplate = (RedisTemplate) SpringContextUtils.getBean("redisTemplateInit");
    private MemberAccountService memberAccountService = SpringContextUtils.getBean(MemberAccountService.class);

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        //过滤无需验证的url
        if (IgnorePathConfig.ignoreRequest(request)) {
            chain.doFilter(request, response);
            return;
        }
        String header = request.getHeader(JwtAuthEnum.AUTHORIZATION.getCode());

        //无token或无header
        if (header == null || !header.startsWith(JwtAuthEnum.AUTH_HEADER.getCode())) {
            OutUtils.print(OutUtils.TOKEN_ERROR_CODE, "NULL TOKEN", JwtAuthEnum.TOKEN_NULL.getCode(), response);
            return;
        }
        //验证token是否正确
        boolean login = login(request);
        if (login) {
            boolean overdue = overdue(request);
            if (overdue) {
                OutUtils.print(OutUtils.TOKEN_ERROR_CODE, "TOKEN OVERDUE", JwtAuthEnum.TOKEN_OVERDUE.getCode(), response);
            } else {
                chain.doFilter(request, response);
            }
        } else {
            OutUtils.print(OutUtils.TOKEN_ERROR_CODE, "PLEASE LOGIN AGAIN", JwtAuthEnum.LOGIN_AGAIN.getCode(), response);
        }
    }

    private boolean getAuthentication(HttpServletRequest request) {
        String token = request.getHeader(JwtAuthEnum.AUTHORIZATION.getCode());
        if (token != null) {
            try {
                String user = Jwts.parser()
                        .setSigningKey(JwtAuthEnum.SIGN_WITH.getCode())
                        .parseClaimsJws(token.replace(JwtAuthEnum.AUTH_HEADER.getCode(), ""))
                        .getBody()
                        .getSubject();

                if (user.equals(MemberAccountUtils.getCurrentMember().getUserId())) {
                    return true;
                }
                return false;
            } catch (Exception e) {
                logger.error("token解析出错:用户错误" + e);
                return false;
            }
        }
        return false;
    }

    /**
     * 验证token是否过期,true=过期,false=未过期
     */
    private boolean overdue(HttpServletRequest request) {
        String token = request.getHeader(JwtAuthEnum.AUTHORIZATION.getCode());
        if (token != null) {
            try {
                Date date = Jwts.parser()
                        .setSigningKey(JwtAuthEnum.SIGN_WITH.getCode())
                        .parseClaimsJws(token.replace(JwtAuthEnum.AUTH_HEADER.getCode(), ""))
                        .getBody()
                        .getExpiration();

                if (date.getTime() < System.currentTimeMillis()) {
                    return true;
                }
                return false;
            } catch (Exception e) {
                logger.error("token解析出错:过期" + e);
                return false;
            }
        }
        return false;
    }

    /**
     * 验证是否登录过
     */
    private boolean login(HttpServletRequest request) {
        String token = request.getHeader(JwtAuthEnum.AUTHORIZATION.getCode()).replace(JwtAuthEnum.AUTH_HEADER.getCode(), "");
        if (token != null) {
            try {
                MemberDTO member = redisTemplate.opsForValue().get(token);
                if (null != member) {
                    redisTemplate.expire(token, 24, TimeUnit.HOURS);
                    MemberAccount account = (MemberAccount) memberAccountService.loadUserByUsername(member.getUserId());
                    UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(account, account.getUsername(), account.getAuthorities());
                    SecurityContextHolder.getContext().setAuthentication(authRequest);
                    request.getSession().setAttribute("member", member);
                    return true;
                } else {
                    return false;
                }
            } catch (Exception e) {
                logger.error("token解析出错:未登录" + e);
                return false;
            }
        }
        return false;
    }

}
